1   /*
2    * Copyright (C) 2008 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect.testing.google;
18  
19  import static com.google.common.base.Preconditions.checkNotNull;
20  import static com.google.common.collect.Lists.newArrayList;
21  import static com.google.common.collect.Sets.newTreeSet;
22  import static com.google.common.collect.testing.SampleElements.Strings.AFTER_LAST;
23  import static com.google.common.collect.testing.SampleElements.Strings.AFTER_LAST_2;
24  import static com.google.common.collect.testing.SampleElements.Strings.BEFORE_FIRST;
25  import static com.google.common.collect.testing.SampleElements.Strings.BEFORE_FIRST_2;
26  import static junit.framework.Assert.assertEquals;
27  
28  import com.google.common.annotations.GwtCompatible;
29  import com.google.common.collect.ContiguousSet;
30  import com.google.common.collect.DiscreteDomain;
31  import com.google.common.collect.ImmutableSet;
32  import com.google.common.collect.ImmutableSortedSet;
33  import com.google.common.collect.Lists;
34  import com.google.common.collect.Ordering;
35  import com.google.common.collect.Range;
36  import com.google.common.collect.Sets;
37  import com.google.common.collect.testing.TestCollectionGenerator;
38  import com.google.common.collect.testing.TestCollidingSetGenerator;
39  import com.google.common.collect.testing.TestIntegerSortedSetGenerator;
40  import com.google.common.collect.testing.TestSetGenerator;
41  import com.google.common.collect.testing.TestStringListGenerator;
42  import com.google.common.collect.testing.TestStringSetGenerator;
43  import com.google.common.collect.testing.TestStringSortedSetGenerator;
44  import com.google.common.collect.testing.TestUnhashableCollectionGenerator;
45  import com.google.common.collect.testing.UnhashableObject;
46  
47  import java.util.Arrays;
48  import java.util.Collections;
49  import java.util.Comparator;
50  import java.util.List;
51  import java.util.Set;
52  import java.util.SortedSet;
53  
54  /**
55   * Generators of different types of sets and derived collections from sets.
56   *
57   * @author Kevin Bourrillion
58   * @author Jared Levy
59   * @author Hayward Chan
60   */
61  @GwtCompatible(emulated = true)
62  public class SetGenerators {
63  
64    public static class ImmutableSetCopyOfGenerator extends TestStringSetGenerator {
65      @Override protected Set<String> create(String[] elements) {
66        return ImmutableSet.copyOf(elements);
67      }
68    }
69  
70    public static class ImmutableSetWithBadHashesGenerator
71        extends TestCollidingSetGenerator
72        // Work around a GWT compiler bug.  Not explicitly listing this will
73        // cause the createArray() method missing in the generated javascript.
74        // TODO: Remove this once the GWT bug is fixed.
75        implements TestCollectionGenerator<Object> {
76      @Override
77      public Set<Object> create(Object... elements) {
78        return ImmutableSet.copyOf(elements);
79      }
80    }
81  
82    public static class DegeneratedImmutableSetGenerator
83        extends TestStringSetGenerator {
84      // Make sure we get what we think we're getting, or else this test
85      // is pointless
86      @SuppressWarnings("cast")
87      @Override protected Set<String> create(String[] elements) {
88        return (ImmutableSet<String>)
89            ImmutableSet.of(elements[0], elements[0]);
90      }
91    }
92  
93    public static class ImmutableSortedSetCopyOfGenerator
94        extends TestStringSortedSetGenerator {
95      @Override protected SortedSet<String> create(String[] elements) {
96        return ImmutableSortedSet.copyOf(elements);
97      }
98    }
99  
100   public static class ImmutableSortedSetHeadsetGenerator
101       extends TestStringSortedSetGenerator {
102     @Override protected SortedSet<String> create(String[] elements) {
103       List<String> list = Lists.newArrayList(elements);
104       list.add("zzz");
105       return ImmutableSortedSet.copyOf(list)
106           .headSet("zzy");
107     }
108   }
109 
110   public static class ImmutableSortedSetTailsetGenerator
111       extends TestStringSortedSetGenerator {
112     @Override protected SortedSet<String> create(String[] elements) {
113       List<String> list = Lists.newArrayList(elements);
114       list.add("\0");
115       return ImmutableSortedSet.copyOf(list)
116           .tailSet("\0\0");
117     }
118   }
119 
120   public static class ImmutableSortedSetSubsetGenerator
121       extends TestStringSortedSetGenerator {
122     @Override protected SortedSet<String> create(String[] elements) {
123       List<String> list = Lists.newArrayList(elements);
124       list.add("\0");
125       list.add("zzz");
126       return ImmutableSortedSet.copyOf(list)
127           .subSet("\0\0", "zzy");
128     }
129   }
130 
131   public static class ImmutableSortedSetExplicitComparator
132       extends TestStringSetGenerator {
133 
134     private static final Comparator<String> STRING_REVERSED
135         = Collections.reverseOrder();
136 
137     @Override protected SortedSet<String> create(String[] elements) {
138       return ImmutableSortedSet.orderedBy(STRING_REVERSED)
139           .add(elements)
140           .build();
141     }
142 
143     @Override public List<String> order(List<String> insertionOrder) {
144       Collections.sort(insertionOrder, Collections.reverseOrder());
145       return insertionOrder;
146     }
147   }
148 
149   public static class ImmutableSortedSetExplicitSuperclassComparatorGenerator
150       extends TestStringSetGenerator {
151 
152     private static final Comparator<Comparable<?>> COMPARABLE_REVERSED
153         = Collections.reverseOrder();
154 
155     @Override protected SortedSet<String> create(String[] elements) {
156       return new ImmutableSortedSet.Builder<String>(COMPARABLE_REVERSED)
157           .add(elements)
158           .build();
159     }
160 
161     @Override public List<String> order(List<String> insertionOrder) {
162       Collections.sort(insertionOrder, Collections.reverseOrder());
163       return insertionOrder;
164     }
165   }
166 
167   public static class ImmutableSortedSetReversedOrderGenerator
168       extends TestStringSetGenerator {
169 
170     @Override protected SortedSet<String> create(String[] elements) {
171       return ImmutableSortedSet.<String>reverseOrder()
172           .addAll(Arrays.asList(elements).iterator())
173           .build();
174     }
175 
176     @Override public List<String> order(List<String> insertionOrder) {
177       Collections.sort(insertionOrder, Collections.reverseOrder());
178       return insertionOrder;
179     }
180   }
181 
182   public static class ImmutableSortedSetUnhashableGenerator
183       extends TestUnhashableSetGenerator {
184     @Override public Set<UnhashableObject> create(
185         UnhashableObject[] elements) {
186       return ImmutableSortedSet.copyOf(elements);
187     }
188   }
189 
190   public static class ImmutableSetAsListGenerator
191       extends TestStringListGenerator {
192     @Override protected List<String> create(String[] elements) {
193       return ImmutableSet.copyOf(elements).asList();
194     }
195   }
196 
197   public static class ImmutableSortedSetAsListGenerator
198       extends TestStringListGenerator {
199     @Override protected List<String> create(String[] elements) {
200       Comparator<String> comparator = createExplicitComparator(elements);
201       ImmutableSet<String> set = ImmutableSortedSet.copyOf(
202           comparator, Arrays.asList(elements));
203       return set.asList();
204     }
205   }
206 
207   public static class ImmutableSortedSetSubsetAsListGenerator
208       extends TestStringListGenerator {
209     @Override protected List<String> create(String[] elements) {
210       Comparator<String> comparator = createExplicitComparator(elements);
211       ImmutableSortedSet.Builder<String> builder
212           = ImmutableSortedSet.orderedBy(comparator);
213       builder.add(BEFORE_FIRST);
214       builder.add(elements);
215       builder.add(AFTER_LAST);
216       return builder.build().subSet(BEFORE_FIRST_2,
217           AFTER_LAST).asList();
218     }
219   }
220 
221   public static class ImmutableSortedSetAsListSubListGenerator
222       extends TestStringListGenerator {
223     @Override protected List<String> create(String[] elements) {
224       Comparator<String> comparator = createExplicitComparator(elements);
225       ImmutableSortedSet.Builder<String> builder
226           = ImmutableSortedSet.orderedBy(comparator);
227       builder.add(BEFORE_FIRST);
228       builder.add(elements);
229       builder.add(AFTER_LAST);
230       return builder.build().asList().subList(1, elements.length + 1);
231     }
232   }
233 
234   public static class ImmutableSortedSetSubsetAsListSubListGenerator
235       extends TestStringListGenerator {
236     @Override protected List<String> create(String[] elements) {
237       Comparator<String> comparator = createExplicitComparator(elements);
238       ImmutableSortedSet.Builder<String> builder
239           = ImmutableSortedSet.orderedBy(comparator);
240       builder.add(BEFORE_FIRST);
241       builder.add(BEFORE_FIRST_2);
242       builder.add(elements);
243       builder.add(AFTER_LAST);
244       builder.add(AFTER_LAST_2);
245       return builder.build().subSet(BEFORE_FIRST_2,
246           AFTER_LAST_2)
247               .asList().subList(1, elements.length + 1);
248     }
249   }
250 
251   public abstract static class TestUnhashableSetGenerator
252       extends TestUnhashableCollectionGenerator<Set<UnhashableObject>>
253       implements TestSetGenerator<UnhashableObject> {
254   }
255 
256   private static Ordering<String> createExplicitComparator(
257       String[] elements) {
258     // Collapse equal elements, which Ordering.explicit() doesn't support, while
259     // maintaining the ordering by first occurrence.
260     Set<String> elementsPlus = Sets.newLinkedHashSet();
261     elementsPlus.add(BEFORE_FIRST);
262     elementsPlus.add(BEFORE_FIRST_2);
263     elementsPlus.addAll(Arrays.asList(elements));
264     elementsPlus.add(AFTER_LAST);
265     elementsPlus.add(AFTER_LAST_2);
266     return Ordering.explicit(Lists.newArrayList(elementsPlus));
267   }
268 
269   /*
270    * All the ContiguousSet generators below manually reject nulls here. In principle, we'd like to
271    * defer that to Range, since it's ContiguousSet.create() that's used to create the sets. However,
272    * that gets messy here, and we already have null tests for Range.
273    */
274 
275   /*
276    * These generators also rely on consecutive integer inputs (not necessarily in order, but no
277    * holes).
278    */
279 
280   // SetCreationTester has some tests that pass in duplicates. Dedup them.
281   private static <E extends Comparable<? super E>> SortedSet<E> nullCheckedTreeSet(E[] elements) {
282     SortedSet<E> set = newTreeSet();
283     for (E element : elements) {
284       // Explicit null check because TreeSet wrongly accepts add(null) when empty.
285       set.add(checkNotNull(element));
286     }
287     return set;
288   }
289 
290   public static class ContiguousSetGenerator extends AbstractContiguousSetGenerator {
291     @Override protected SortedSet<Integer> create(Integer[] elements) {
292       return checkedCreate(nullCheckedTreeSet(elements));
293     }
294   }
295 
296   public static class ContiguousSetHeadsetGenerator extends AbstractContiguousSetGenerator {
297     @Override protected SortedSet<Integer> create(Integer[] elements) {
298       SortedSet<Integer> set = nullCheckedTreeSet(elements);
299       int tooHigh = (set.isEmpty()) ? 0 : set.last() + 1;
300       set.add(tooHigh);
301       return checkedCreate(set).headSet(tooHigh);
302     }
303   }
304 
305   public static class ContiguousSetTailsetGenerator extends AbstractContiguousSetGenerator {
306     @Override protected SortedSet<Integer> create(Integer[] elements) {
307       SortedSet<Integer> set = nullCheckedTreeSet(elements);
308       int tooLow = (set.isEmpty()) ? 0 : set.first() - 1;
309       set.add(tooLow);
310       return checkedCreate(set).tailSet(tooLow + 1);
311     }
312   }
313 
314   public static class ContiguousSetSubsetGenerator extends AbstractContiguousSetGenerator {
315     @Override protected SortedSet<Integer> create(Integer[] elements) {
316       SortedSet<Integer> set = nullCheckedTreeSet(elements);
317       if (set.isEmpty()) {
318         /*
319          * The (tooLow + 1, tooHigh) arguments below would be invalid because tooLow would be
320          * greater than tooHigh.
321          */
322         return ContiguousSet.create(Range.openClosed(0, 1), DiscreteDomain.integers()).subSet(0, 1);
323       }
324       int tooHigh = set.last() + 1;
325       int tooLow = set.first() - 1;
326       set.add(tooHigh);
327       set.add(tooLow);
328       return checkedCreate(set).subSet(tooLow + 1, tooHigh);
329     }
330   }
331 
332   private abstract static class AbstractContiguousSetGenerator
333       extends TestIntegerSortedSetGenerator {
334     protected final ContiguousSet<Integer> checkedCreate(SortedSet<Integer> elementsSet) {
335       List<Integer> elements = newArrayList(elementsSet);
336       /*
337        * A ContiguousSet can't have holes. If a test demands a hole, it should be changed so that it
338        * doesn't need one, or it should be suppressed for ContiguousSet.
339        */
340       for (int i = 0; i < elements.size() - 1; i++) {
341         assertEquals(elements.get(i) + 1, (int) elements.get(i + 1));
342       }
343       Range<Integer> range =
344           (elements.isEmpty()) ? Range.closedOpen(0, 0) : Range.encloseAll(elements);
345       return ContiguousSet.create(range, DiscreteDomain.integers());
346     }
347   }
348 }
349